﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using lumo;
using game.character;
using lumo.display.object2D;
using Microsoft.Xna.Framework.Graphics;
using lumo.display.light;
using Microsoft.Xna.Framework;
using lumo.display.animation2D;

namespace game.display.object2d
{
    /// <summary>
    /// 
    /// </summary>
    public class SpritesetMap
    {
        public Tilemap                 Tilemap; 
        RenderTarget2D        TempRenderTarget;
        Effect                LightMapEffect;
        Effect                WaterEffect;
        Texture2D             WaterNormal;
        BlendState            Multiplicative;
        List<SpriteCharacter> SpriteCharacterList = new List<SpriteCharacter>();
        List<game.display.effect.Blur> BlurList = new List<game.display.effect.Blur>();

        /// <summary>
        /// 
        /// </summary>
        /// <param name="level"></param>
        public SpritesetMap(LumoComponent Component, GameMap map)
        {
            // -- Set variables.
            // Save the level.
            Tilemap = new Tilemap(Component, map.Tiled);
            // Create the render target.
            TempRenderTarget = new RenderTarget2D(Component.GraphicsDevice, (int)(Global.DefaultScreenSize.X + Global.AditionalTiles * 32), (int)(Global.DefaultScreenSize.Y + Global.AditionalTiles * 32));
            // Effects
            LightMapEffect = Component.Content.Load<Effect>("Effects/Light/LightMap");
            WaterEffect    = Component.Content.Load<Effect>("Effects/Water");
            WaterNormal    = Component.Content.Load<Texture2D>("Effects/WaterNormal");
            // Set multiplicative blend state.
            Multiplicative = new BlendState();
            Multiplicative.ColorSourceBlend = Blend.DestinationColor;
            Multiplicative.ColorDestinationBlend = Blend.Zero;
            Multiplicative.AlphaSourceBlend = Blend.DestinationColor;
            Multiplicative.AlphaDestinationBlend = Blend.Zero;

            // Set Blur List
            // Blur in 128x128
            BlurList.Add(new game.display.effect.Blur(Component, 128, 128));
            // Blur in 256x256
            BlurList.Add(new game.display.effect.Blur(Component, 256, 256));
            // Blur in 512x512
            BlurList.Add(new game.display.effect.Blur(Component, 512, 512));

            // Add characters.
            foreach (GameCharacter enemy in map.Events )
                SpriteCharacterList.Add(new SpriteCharacter(enemy));
            SpriteCharacterList.Add(new SpriteCharacter(map.Player));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="component"></param>
        public void Update(LumoComponent Component, Camera camera)
        {
            Tilemap.Update(Component, camera);

            // Update level.
            foreach (SpriteCharacter sprite in SpriteCharacterList)
                sprite.Update(Component);

            // Update effect parameters.
            float offset = (float)(Component.GameTime.TotalGameTime.TotalSeconds / 32.0f);
            float offsetX = (float)(offset + camera.X / (Global.DefaultScreenSize.X + Global.AditionalTiles * 32));
            float offsetY = (float)(offset + camera.Y / (Global.DefaultScreenSize.Y + Global.AditionalTiles * 32));
            WaterEffect.Parameters["offsetX"].SetValue((float)(offsetX - Math.Floor(offsetX)));
            WaterEffect.Parameters["offsetY"].SetValue((float)(offsetY - Math.Floor(offsetY)));
        }

        /// <summary>
        /// Draw the object.
        /// </summary>
        /// <param name="spriteBatch">The spritebatch</param>
        public void Draw(LumoComponent Component, LightManager LightManager = null)
        {
            // Set variables
            GraphicsDevice GraphicsDevice = Component.GraphicsDevice;
            SpriteBatch    SpriteBatch    = Component.SpriteBatch;

            // Matrix transformation.
            Matrix Transform = Matrix.CreateTranslation(Global.AditionalTiles * 16, Global.AditionalTiles * 16, 0);

            // Check for light manager.
            if (LightManager != null)
            {
                // Draw everything that cast shadow.
                GraphicsDevice.SetRenderTarget(TempRenderTarget);
                GraphicsDevice.Clear(new Color(0, 0, 0, 0));
                Component.SpriteBatch.Begin(SpriteSortMode.Immediate, null, SamplerState.PointClamp, DepthStencilState.None, null, null, Transform);
                {
                    Tilemap.DrawCastShadow(SpriteBatch);
                    foreach (SpriteCharacter sprite in SpriteCharacterList)
                        if (sprite.CastShadow)
                            sprite.Draw(SpriteBatch);
                }
                Component.SpriteBatch.End();

                // Get the light map.
                Texture2D LightMap = LightManager.Apply(TempRenderTarget);

                // APply Blur.
                foreach (game.display.effect.Blur blur in BlurList)
                    LightMap = blur.Apply(LightMap);

                // Draw the rest of the scene.
                DrawDefault(GraphicsDevice, SpriteBatch, ref Transform);

                // Multiplicate the light.
                Component.SpriteBatch.Begin(SpriteSortMode.Immediate, Multiplicative, SamplerState.PointClamp, DepthStencilState.None, null, LightMapEffect, Transform);
                SpriteBatch.Draw(LightMap, new Rectangle(-16 * Global.AditionalTiles, -16 * Global.AditionalTiles, (int)Global.DefaultScreenSize.X + Global.AditionalTiles * 32, (int)Global.DefaultScreenSize.Y + Global.AditionalTiles * 32), LightManager.Color);
                Component.SpriteBatch.End();
            }
            else
            {
                // Draw the rest of the scene.
                DrawDefault(GraphicsDevice, SpriteBatch, ref Transform);
            }

            // Draw the map using the water effect.
            DrawWaterEffect(GraphicsDevice, SpriteBatch);
        }


        /// <summary>
        /// Draw the default scene.
        /// </summary>
        private void DrawDefault(GraphicsDevice GraphicsDevice, SpriteBatch SpriteBatch, ref Matrix Transform)
        {
            // Draw the normal scene
            GraphicsDevice.SetRenderTarget(TempRenderTarget);

            GraphicsDevice.Clear(new Color(0, 104, 178));

            SpriteBatch.Begin(SpriteSortMode.Immediate, null, null, null, null, null, Transform);
            Tilemap.DrawBack(SpriteBatch);
            SpriteBatch.End();

            SpriteBatch.Begin(SpriteSortMode.Immediate, null, null, null, null, null, Transform);
            {
                foreach (SpriteCharacter sprite in SpriteCharacterList)
                    if (!sprite.HighPriority)
                        sprite.Draw(SpriteBatch);
                Tilemap.DrawMiddle(SpriteBatch);
                foreach (SpriteCharacter sprite in SpriteCharacterList)
                    if (sprite.HighPriority)
                        sprite.Draw(SpriteBatch);
            }
            SpriteBatch.End();

            SpriteBatch.Begin(SpriteSortMode.Immediate, null, null, null, null, null, Transform * Matrix.CreateScale(2) *
                Matrix.CreateTranslation(new Vector3(-TempRenderTarget.Width / 2, -TempRenderTarget.Height / 2, 0)));
            Tilemap.DrawFront(SpriteBatch);
            SpriteBatch.End();
        }


        /// <summary>
        /// Draw the water effect.
        /// </summary>
        private void DrawWaterEffect(GraphicsDevice GraphicsDevice, SpriteBatch SpriteBatch)
        {
            // Draw the normal scene
            GraphicsDevice.SetRenderTarget(null);
            GraphicsDevice.Clear(Color.Black);
            SpriteBatch.Begin(SpriteSortMode.Immediate, null, null, null, null, WaterEffect, Global.ScreenMatrix);
            {
                GraphicsDevice.Textures[1] = WaterNormal;
                SpriteBatch.Draw(TempRenderTarget, new Vector2(-16 * Global.AditionalTiles, -16 * Global.AditionalTiles), Color.White);
                GraphicsDevice.Textures[1] = null;
            }
            SpriteBatch.End();
        }
    }
}
